package com.cli.springboottemplateengine.engine.analyze;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.builder.AstBuilder;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.control.CompilePhase;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class GroovyTemplateVariableExtractor implements TemplateVariableAnalyzer {

    private static final Pattern GROOVY_CODE_PATTERN = Pattern.compile("<%([\\s\\S]*?)%>|\\$\\{([\\s\\S]*?)\\}");

    @Override
    public Set<String> extractVariables(String templateContent) {
        // 1. 提取所有 Groovy 代码片段
        List<String> codeSnippets = new ArrayList<>();
        Matcher matcher = GROOVY_CODE_PATTERN.matcher(templateContent);
        while (matcher.find()) {
            String scriptlet = matcher.group(1);
            String expression = matcher.group(2);
            if (scriptlet != null) codeSnippets.add(scriptlet);
            if (expression != null) codeSnippets.add(expression);
        }

        if (codeSnippets.isEmpty()) {
            return Collections.emptySet();
        }

        String fullScript = codeSnippets.stream().collect(Collectors.joining(";\n"));

        // 2. 构建 AST
        AstBuilder astBuilder = new AstBuilder();
        List<ASTNode> nodes = astBuilder.buildFromString(CompilePhase.SEMANTIC_ANALYSIS, true, fullScript);

        // 3. 访问 AST 并收集所有潜在路径和局部声明
        FullPathVisitor visitor = new FullPathVisitor();
        for (ASTNode node : nodes) {
            node.visit(visitor);
        }

        // 4. 后处理 - 过滤路径
        return filterPaths(visitor.getPotentialPaths(), visitor.getScopeDeclarations());
    }

    /**
     * 过滤路径，移除局部变量路径和中间路径
     */
    private Set<String> filterPaths(Set<String> potentialPaths, Set<String> scopeDeclarations) {
        // 步骤 1: 过滤掉由局部变量开头的路径
        Set<String> nonLocalPaths = potentialPaths.stream()
                .filter(path -> {
                    String root = getPathRoot(path);
                    return !scopeDeclarations.contains(root) && !isSpecialVariable(root);
                })
                .collect(Collectors.toSet());

        // 步骤 2: 过滤掉作为其他路径一部分的中间路径
        // 例如，如果存在 "user.address.city"，则移除 "user.address" 和 "user"
        Set<String> finalPaths = new HashSet<>(nonLocalPaths);
        nonLocalPaths.forEach(path1 -> {
            nonLocalPaths.forEach(path2 -> {
                if (!path1.equals(path2) && path1.startsWith(path2 + ".")) {
                    finalPaths.remove(path2);
                }
            });
        });

        return finalPaths;
    }

    private String getPathRoot(String path) {
        int dotIndex = path.indexOf('.');
        if (dotIndex == -1) {
            return path;
        }
        return path.substring(0, dotIndex);
    }

    private boolean isSpecialVariable(String name) {
        return "out".equals(name) || "binding".equals(name) || "it".equals(name);
    }

    /**
     * AST 访问者，用于提取所有潜在的全路径变量
     */
    private static class FullPathVisitor extends CodeVisitorSupport {
        private final Set<String> potentialPaths = new HashSet<>();
        private final Set<String> scopeDeclarations = new HashSet<>();

        public Set<String> getPotentialPaths() { return potentialPaths; }
        public Set<String> getScopeDeclarations() { return scopeDeclarations; }

        @Override
        public void visitPropertyExpression(PropertyExpression expression) {
            potentialPaths.add(reconstructPath(expression));
            // 继续访问，以确保能捕获到更深层次的调用，如 user.info.getAge()
            super.visitPropertyExpression(expression);
        }

        @Override
        public void visitMethodCallExpression(MethodCallExpression call) {
            // 将方法调用也视为路径的一部分, e.g., "items.size()" -> "items.size"
            String path = reconstructPath(call);
            if(path != null && !path.isEmpty()) {
                potentialPaths.add(path);
            }

            // 必须继续访问，因为方法的参数也可能是变量
            super.visitMethodCallExpression(call);
        }

        @Override
        public void visitVariableExpression(VariableExpression expression) {
            potentialPaths.add(expression.getName());
            super.visitVariableExpression(expression);
        }

        @Override
        public void visitDeclarationExpression(DeclarationExpression expression) {
            if (expression.getLeftExpression() instanceof VariableExpression) {
                scopeDeclarations.add(((VariableExpression) expression.getLeftExpression()).getName());
            }
            // Tuple (e.g., def (a,b) = [1,2]) is not handled here for simplicity
            super.visitDeclarationExpression(expression);
        }

        @Override
        public void visitForLoop(ForStatement forLoop) {
            scopeDeclarations.add(forLoop.getVariable().getName());
            super.visitForLoop(forLoop);
        }

        /**
         * 递归地从表达式重构完整的路径字符串
         */
        private String reconstructPath(Expression expr) {
            if (expr instanceof PropertyExpression) {
                PropertyExpression propExpr = (PropertyExpression) expr;
                return reconstructPath(propExpr.getObjectExpression()) + "." + propExpr.getPropertyAsString();
            } else if (expr instanceof MethodCallExpression) {
                MethodCallExpression callExpr = (MethodCallExpression) expr;
                // objectExpression可能是隐式的'this'，在这种情况下它是一个VariableExpression("this")
                String objectPath = reconstructPath(callExpr.getObjectExpression());

                String methodAndParens = callExpr.getMethodAsString() + "()";

                // 忽略this前缀
                if ("this".equals(objectPath)) {
                    return methodAndParens;
                }
                return objectPath + "." + methodAndParens;
            } else if (expr instanceof VariableExpression) {
                return ((VariableExpression) expr).getName();
            }
            return "";
        }
    }
}